import logging
import yaml
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import mysql.connector
from mysql.connector import Error
import time

# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class DoubanMovieCommentsScraper:
    def __init__(self, driver_path=None):
        options = webdriver.EdgeOptions()
        if driver_path:
            options.binary_location = driver_path
        self.driver = webdriver.Edge(options=options)
        self.config = self.load_config('config.yaml')
        self.login_douban()
        self.db_connection = self.connect_to_database()

    def load_config(self, config_file):
        with open(config_file, 'r', encoding='utf-8') as file:
            return yaml.safe_load(file)

    def __del__(self):
        self.close_browser()
        self.close_database_connection()

    def open_page(self, url):
        try:
            self.driver.get(url)
        except TimeoutException as e:
            logging.error(f"由于超时无法打开页面: {e}")
            return False
        except Exception as e:
            logging.error(f"无法打开页面: {e}")
            return False
        return True

    def wait_for_element(self, by, value, timeout=10):
        try:
            return WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located((by, value)))
        except TimeoutException:
            logging.error(f"在 {timeout} 秒内未找到 {by}='{value}' 的元素")
            return None

    def wait_for_elements(self, by, value, timeout=10):
        try:
            return WebDriverWait(self.driver, timeout).until(EC.presence_of_all_elements_located((by, value)))
        except TimeoutException:
            logging.error(f"在 {timeout} 秒内未找到任何 {by}='{value}' 的元素")
            return []

    def get_element_text(self, element, css_selector, default='N/A'):
        try:
            return element.find_element(By.CSS_SELECTOR, css_selector).text
        except NoSuchElementException:
            return default

    def get_element_attribute(self, element, attribute, default='N/A'):
        try:
            return element.get_attribute(attribute)
        except NoSuchElementException:
            return default

    def extract_comment_details(self, comment):
        comment_id = self.get_element_attribute(comment, 'data-cid')

        user_info = comment.find_element(By.CSS_SELECTOR, 'div.avatar a')
        username = self.get_element_attribute(user_info, 'title')
        user_link = self.get_element_attribute(user_info, 'href')

        avatar = self.get_element_attribute(user_info.find_element(By.TAG_NAME, 'img'), 'src')

        # 确保有用人数存储在 span.votes.vote-count 中
        useful_count = self.get_element_text(comment, 'span.votes.vote-count', default='0')

        comment_text = self.get_element_text(comment, 'span.short')

        # 处理 span.rating 可能不出现的情况
        rating_element = comment.find_elements(By.CSS_SELECTOR, 'span.rating')
        rating = rating_element[0].get_attribute('title') if rating_element else 'N/A'

        comment_time = self.get_element_attribute(comment.find_element(By.CSS_SELECTOR, 'span.comment-time'), 'title')

        comment_location = self.get_element_text(comment, 'span.comment-location')

        return {
            "comment_id": comment_id,
            "username": username,
            "user_link": user_link,
            "avatar": avatar,
            "useful_count": useful_count,  # 添加有用人数到字典中
            "comment_text": comment_text,
            "rating": rating,
            "comment_time": comment_time,
            "comment_location": comment_location
        }

    def extract_comments(self):
        comments = self.wait_for_elements(By.CSS_SELECTOR, 'div.comment-item')
        for comment in comments:
            try:
                comment_details = self.extract_comment_details(comment)
                self.insert_comment_to_db(comment_details)
            except NoSuchElementException as e:
                logging.error(f"评论中未找到元素: {e}")

    def click_next_page(self):
        try:
            next_button = self.wait_for_element(By.CSS_SELECTOR, 'a.next')
            if next_button and 'disabled' not in next_button.get_attribute('class'):
                next_button.click()
                return True
            else:
                logging.info("没有更多页面可抓取。")
                return False
        except NoSuchElementException as e:
            logging.error(f"未找到下一页按钮: {e}")
            return False
        except Exception as e:
            logging.error(f"点击下一页失败: {e}")
            return False

    def scrape_all_pages(self, url):
        if not self.open_page(url):
            return
        while True:
            self.extract_comments()
            if not self.click_next_page():
                break
        self.close_browser()

    def close_browser(self):
        self.driver.quit()

    def login_douban(self):
        login_url = self.config['douban']['login_url']
        username = self.config['douban']['username']
        password = self.config['douban']['password']

        if not self.open_page(login_url):
            return

        try:
            email_input = self.wait_for_element(By.ID, 'email')
            password_input = self.wait_for_element(By.ID, 'password')
            submit_button = self.wait_for_element(By.NAME, 'login')

            if email_input and password_input and submit_button:
                email_input.send_keys(username)
                password_input.send_keys(password)
                submit_button.click()

                # 等待登录成功后的页面元素加载完成
                WebDriverWait(self.driver, 60).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.comment-item')))
                logging.info("成功登录豆瓣")
            else:
                logging.error("登录表单元素未找到")
        except Exception as e:
            logging.error(f"登录过程中发生错误: {e}")

    def connect_to_database(self):
        try:
            connection = mysql.connector.connect(
                host=str(self.config['database']['host']),
                database=str(self.config['database']['database']),
                user=str(self.config['database']['user']),
                password=str(self.config['database']['password'])
            )
            if connection.is_connected():
                logging.info("成功连接到数据库")
                return connection
        except Error as e:
            logging.error(f"数据库连接失败: {e}")
            return None

    def close_database_connection(self):
        if self.db_connection and self.db_connection.is_connected():
            self.db_connection.close()
            logging.info("数据库连接已关闭")

    def insert_comment_to_db(self, comment_details):
        query = """
        INSERT INTO comments (comment_id, username, user_link, avatar, useful_count, comment_text, rating, comment_time, comment_location)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON DUPLICATE KEY UPDATE
        username=VALUES(username),
        user_link=VALUES(user_link),
        avatar=VALUES(avatar),
        useful_count=VALUES(useful_count),
        comment_text=VALUES(comment_text),
        rating=VALUES(rating),
        comment_time=VALUES(comment_time),
        comment_location=VALUES(comment_location)
        """
        values = (
            comment_details['comment_id'],
            comment_details['username'],
            comment_details['user_link'],
            comment_details['avatar'],
            int(comment_details['useful_count']),
            comment_details['comment_text'],
            comment_details['rating'],
            comment_details['comment_time'],
            comment_details['comment_location']
        )
        try:
            cursor = self.db_connection.cursor()
            cursor.execute(query, values)
            self.db_connection.commit()
            logging.info(f"评论 {comment_details['comment_id']} 插入成功")
        except Error as e:
            logging.error(f"插入评论时出错: {e}")
        finally:
            if cursor:
                cursor.close()

if __name__ == "__main__":
    scraper = DoubanMovieCommentsScraper()
    scraper.scrape_all_pages("https://movie.douban.com/subject/1292064/comments?status=P")
